// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package modload

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"go/build"
	"internal/lazyregexp"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"

	"cmd/go/internal/base"
	"cmd/go/internal/cfg"
	"cmd/go/internal/fsys"
	"cmd/go/internal/lockedfile"
	"cmd/go/internal/modconv"
	"cmd/go/internal/modfetch"
	"cmd/go/internal/search"

	"golang.org/x/mod/modfile"
	"golang.org/x/mod/module"
	"golang.org/x/mod/semver"
)

// 由其他包设置的变量。
// None
// TODO（#40775）：看看这些参数是否可以作为显式参数。
var (
	// RootMode确定是否需要模块根。
	RootMode Root

	// ForceUseModules可设置为在以下情况下强制启用模块：
	// GO111MODULE=auto或在GO111MODULE=off时报告错误。
	ForceUseModules bool

	allowMissingModuleImports bool
)

// 在Init中设置的变量。
var (
	initialized bool
	modRoot     string
	gopath      string
)

// 在initTarget中设置的变量（在{Load，Create}ModFile期间）。
var (
	Target module.Version

	// targetPrefix是目标中包的路径前缀，不带尾随字符
	// 斜线对于大多数模块，targetPrefix只是Target.Path，但是
	// 标准库模块“std”的前缀为空。
	targetPrefix string

	// targetInGorootSrc缓存modRoot是否在GOROOT/src中。
	// “std”模块在GOROOT/src中是特殊的，但在其他方面并非如此。
	targetInGorootSrc bool
)

type Root int

const (
	// 自动启动是大多数命令的默认设置。Init将查找
	// 当前目录或任何父目录中的go.mod文件。如果没有找到，
	// 模块可能被禁用（GO111MODULE=auto），或者命令可能在系统中运行
	// 有限模块模式。
	AutoRoot Root = iota

	// NoRoot用于在模块模式下运行并忽略任何go.mod的命令
	// 将当前目录或父目录中的文件归档。
	NoRoot

	// NeedRoot用于必须在模块模式下运行且不在模块模式下运行的命令
	// 没有主模块也有意义。
	NeedRoot
)

// ModFile返回解析后的go.mod文件。
// None
// 请注意，在调用LoadPackages或LoadModGraph之后，
// modfile.File中的require语句不再存在
// 真相的来源将被忽略：直接进行编辑
// 将在下次调用WriteGoMod时丢失。
// 对require语句进行永久性更改
// 在go.mod中，在加载前对其进行编辑。
func ModFile() *modfile.File {
	Init()
	if modFile == nil {
		die()
	}
	return modFile
}

func BinDir() string {
	Init()
	return filepath.Join(gopath, "bin")
}

// Init确定模块模式是否已启用，并定位
// 当前模块（如果有），为Git子流程设置环境变量，以及
// 配置cfg、codehost、load、modfetch和search包以供使用
// 使用模块。
func Init() {
	if initialized {
		return
	}
	initialized = true

	// 与WillBeEnabled保持同步。我们在这里执行额外的验证
	// 有很多诊断和副作用，所以我们不能使用
	// 威尔被直接逮捕了。
	var mustUseModules bool
	env := cfg.Getenv("GO111MODULE")
	switch env {
	default:
		base.Fatalf("go: unknown environment setting GO111MODULE=%s", env)
	case "auto":
		mustUseModules = ForceUseModules
	case "on", "":
		mustUseModules = true
	case "off":
		if ForceUseModules {
			base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
		}
		mustUseModules = false
		return
	}

	if err := fsys.Init(base.Cwd()); err != nil {
		base.Fatalf("go: %v", err)
	}

	// 禁用Git对密码的任何提示。
	// 仅对2.3.0或更高版本有效，但避免
	// 早期版本中的提示太难了。
	// 如果用户已显式设置GIT_TERMINAL_PROMPT=1，请保留
	// 提示。
	// 见golang.org/issue/9341和golang.org/issue/12706。
	if os.Getenv("GIT_TERMINAL_PROMPT") == "" {
		os.Setenv("GIT_TERMINAL_PROMPT", "0")
	}

	// 通过Git禁用任何ssh连接池。
	// 如果Git子进程将子进程分叉到后台以缓存新连接，
	// 那个孩子开着门。Git子流程退出后，
	// os/exec希望能够从stdout/stderr管道读取数据
	// 直到EOF，才能获取Git子进程在退出之前写入的所有数据。
	// 直到孩子也退出，EOF才会出现，因为孩子
	// 正在固定管道的写入端。
	// 这是不幸的，但至少出现过两次
	// （见golang.org/issue/13453和golang.org/issue/16104）
	// 当它这样做时，会让用户感到困惑。
	// 如果用户已显式设置GIT_SSH或GIT_SSH_命令，
	// 假设他们知道自己在做什么，不要踩在上面。
	// 但默认为关闭ControlMaster。
	if os.Getenv("GIT_SSH") == "" && os.Getenv("GIT_SSH_COMMAND") == "" {
		os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no -o BatchMode=yes")
	}

	if os.Getenv("GCM_INTERACTIVE") == "" {
		os.Setenv("GCM_INTERACTIVE", "never")
	}

	if modRoot != "" {
		// 调用Init之前的modRoot集（“go mod Init”执行此操作）。
		// 无需搜索go.mod。
	} else if RootMode == NoRoot {
		if cfg.ModFile != "" && !base.InGOFLAGS("-modfile") {
			base.Fatalf("go: -modfile cannot be used with commands that ignore the current module")
		}
		modRoot = ""
	} else {
		modRoot = findModuleRoot(base.Cwd())
		if modRoot == "" {
			if cfg.ModFile != "" {
				base.Fatalf("go: cannot find main module, but -modfile was set.\n\t-modfile cannot be used to set the module root directory.")
			}
			if RootMode == NeedRoot {
				base.Fatalf("go: %v", ErrNoModRoot)
			}
			if !mustUseModules {
				// GO111模块为“自动”，我们找不到模块根。
				// 保持GOPATH模式。
				return
			}
		} else if search.InDir(modRoot, os.TempDir()) == "." {
			// 如果创建/tmp/go.mod进行实验，
			// 然后是在/tmp下创建工作目录的所有测试
			// 会在他们不期望的时候找到它并获取模块。
			// 这是一个有点奇怪的事情，不允许，但相当神秘
			// 当它发生的时候。见golang.org/issue/26708。
			modRoot = ""
			fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
			if !mustUseModules {
				return
			}
		}
	}
	if cfg.ModFile != "" && !strings.HasSuffix(cfg.ModFile, ".mod") {
		base.Fatalf("go: -modfile=%s: file does not have .mod extension", cfg.ModFile)
	}

	// 我们处于模块模式。设置任何需要设置的全局变量。
	cfg.ModulesEnabled = true
	setDefaultBuildMod()
	list := filepath.SplitList(cfg.BuildContext.GOPATH)
	if len(list) == 0 || list[0] == "" {
		base.Fatalf("missing $GOPATH")
	}
	gopath = list[0]
	if _, err := fsys.Stat(filepath.Join(gopath, "go.mod")); err == nil {
		base.Fatalf("$GOPATH/go.mod exists but should not")
	}

	if modRoot == "" {
		// 我们处于模块模式，但不在模块内。
		// None
		// 像“go build”、“go run”、“go list”这样的命令没有go.mod文件
		// 读或写。他们需要找到并下载最新版本
		// 无法保存版本的潜在大量模块
		// 信息我们可以慢慢地（但不是重复地）成功，但那是不可能的
		// 通常不是一次好的经历。
		// None
		// 相反，我们禁止解析std和std以外的模块的导入路径
		// cmd。用户仍可以在上生成使用.go文件指定的包
		// 命令行，但如果这些文件导入任何内容，他们将看到错误
		// 外性病。
		// None
		// 这可以通过调用AllowMissingModuleImports来覆盖。
		// 例如，“go get”就是这样做的，因为它需要解析路径。
		// None
		// 见golang.org/issue/32027。
	} else {
		modfetch.GoSumFile = strings.TrimSuffix(ModFilePath(), ".mod") + ".sum"
		search.SetModRoot(modRoot)
	}
}

// WillBeEnabled检查是否应启用模块，但不应启用
// 通过安装钩子初始化模块。如果已经调用了Init，
// WillBeEnabled返回与Enabled相同的结果。
// None
// 打破一个循环需要此功能。主包需要知道
// 是否启用模块以安装模块或GOPATH版本
// 但Init读取“goget”中的-modfile标志，所以它不应该
// 在安装命令并解析标志之前，无法调用。而不是
// 通过调用Init和Enabled，主包可以调用此函数。
func WillBeEnabled() bool {
	if modRoot != "" || cfg.ModulesEnabled {
		// 已启用。
		return true
	}
	if initialized {
		// 已初始化，未启用。
		return false
	}

	// 与Init保持同步。Init执行额外的验证并打印警告或
	// 退出，因此无法直接调用此函数。
	env := cfg.Getenv("GO111MODULE")
	switch env {
	case "on", "":
		return true
	case "auto":
		break
	default:
		return false
	}

	if modRoot := findModuleRoot(base.Cwd()); modRoot == "" {
		// GO111模块为“自动”，我们找不到模块根。
		// 保持GOPATH模式。
		return false
	} else if search.InDir(modRoot, os.TempDir()) == "." {
		// 如果创建/tmp/go.mod进行实验，
		// 然后是在/tmp下创建工作目录的所有测试
		// 会在他们不期望的时候找到它并获取模块。
		// 这是一个有点奇怪的事情，不允许，但相当神秘
		// 当它发生的时候。见golang.org/issue/26708。
		return false
	}
	return true
}

// 已启用报告模块是否已启用（或必须启用）。
// 如果模块已启用，但没有主模块，enabled返回true
// 然后第一次使用模块信息就会调用die
// （通常通过MustModRoot）。
func Enabled() bool {
	Init()
	return modRoot != "" || cfg.ModulesEnabled
}

// ModRoot返回主模块的根。
// 如果没有主模块，则调用base.Fatalf。
func ModRoot() string {
	if !HasModRoot() {
		die()
	}
	return modRoot
}

// HasModRoot报告主模块是否存在。
// 即使Enabled返回true，HasModRoot也可能返回false：例如，“get”
// 不需要主模块。
func HasModRoot() bool {
	Init()
	return modRoot != ""
}

// ModFilePath返回go.mod文件的有效路径。通常情况下
// ModRoot返回的目录中的“go.mod”，但是-modfile标志可能会
// 改变它的位置。如果没有main，ModFilePath调用base.Fatalf
// 模块，即使设置了-modfile。
func ModFilePath() string {
	if !HasModRoot() {
		die()
	}
	if cfg.ModFile != "" {
		return cfg.ModFile
	}
	return filepath.Join(modRoot, "go.mod")
}

func die() {
	if cfg.Getenv("GO111MODULE") == "off" {
		base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
	}
	if dir, name := findAltConfig(base.Cwd()); dir != "" {
		rel, err := filepath.Rel(base.Cwd(), dir)
		if err != nil {
			rel = dir
		}
		cdCmd := ""
		if rel != "." {
			cdCmd = fmt.Sprintf("cd %s && ", rel)
		}
		base.Fatalf("go: cannot find main module, but found %s in %s\n\tto create a module there, run:\n\t%sgo mod init", name, dir, cdCmd)
	}
	base.Fatalf("go: %v", ErrNoModRoot)
}

var ErrNoModRoot = errors.New("go.mod file not found in current directory or any parent directory; see 'go help modules'")

type goModDirtyError struct{}

func (goModDirtyError) Error() string {
	if cfg.BuildModExplicit {
		return fmt.Sprintf("updates to go.mod needed, disabled by -mod=%v; to update it:\n\tgo mod tidy", cfg.BuildMod)
	}
	if cfg.BuildModReason != "" {
		return fmt.Sprintf("updates to go.mod needed, disabled by -mod=%s\n\t(%s)\n\tto update it:\n\tgo mod tidy", cfg.BuildMod, cfg.BuildModReason)
	}
	return "updates to go.mod needed; to update it:\n\tgo mod tidy"
}

var errGoModDirty error = goModDirtyError{}

// LoadModFile设置目标，如果有主模块，则解析初始值
// 从其go.mod文件生成列表。
// None
// LoadModFile可能会在内存中进行更改，例如添加go指令和
// 确保需求一致，并将这些更改写回
// 除非DisallowWriteGoMod生效，否则为磁盘。
// None
// 作为副作用，LoadModFile可能会在以下情况下将cfg.BuildMod更改为“vendor”
// -mod未明确设置，应启用自动售货。
// None
// 如果已调用LoadModFile或CreateModFile，LoadModFile将返回
// 现有的内存需求（而不是从磁盘重新读取它们）。
// None
// LoadModFile检查模块图的根与每个根的一致性
// 其他，但与LoadModGraph不同，LoadModGraph不加载完整模块图或检查
// 它有助于全球一致性。modload包之外的大多数调用者应该
// 改用LoadModGraph。
func LoadModFile(ctx context.Context) *Requirements {
	rs, needCommit := loadModFile(ctx)
	if needCommit {
		commitRequirements(ctx, modFileGoVersion(), rs)
	}
	return rs
}

// loadModFile类似于loadModFile，但不隐式提交
// 在修复不一致性后，要求返回磁盘。
// None
// 如果needCommit为true，则在调用方对
// 返回的需求他们应该调用CommitRequests来修复任何
// 磁盘上go.mod文件中可能存在的不一致。
func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
	if requirements != nil {
		return requirements, false
	}

	Init()
	if modRoot == "" {
		Target = module.Version{Path: "command-line-arguments"}
		targetPrefix = "command-line-arguments"
		goVersion := LatestGoVersion()
		rawGoVersion.Store(Target, goVersion)
		requirements = newRequirements(modDepthFromGoVersion(goVersion), nil, nil)
		return requirements, false
	}

	gomod := ModFilePath()
	var data []byte
	var err error
	if gomodActual, ok := fsys.OverlayPath(gomod); ok {
		// 如果go.mod是覆盖的一部分，请不要锁定它。
		// 在计划9中，锁定需要chmod，我们不想修改任何文件
		// 在覆盖层中。见#44700。
		data, err = os.ReadFile(gomodActual)
	} else {
		data, err = lockedfile.Read(gomodActual)
	}
	if err != nil {
		base.Fatalf("go: %v", err)
	}

	var fixed bool
	f, err := modfile.Parse(gomod, data, fixVersion(ctx, &fixed))
	if err != nil {
		// modfile返回错误。解析以file:line开头。
		base.Fatalf("go: errors parsing go.mod:\n%s\n", err)
	}
	if f.Module == nil {
		// 没有模块声明。必须添加模块路径。
		base.Fatalf("go: no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod")
	}

	modFile = f
	initTarget(f.Module.Mod)
	index = indexModFile(data, f, fixed)

	if err := module.CheckImportPath(f.Module.Mod.Path); err != nil {
		if pathErr, ok := err.(*module.InvalidPathError); ok {
			pathErr.Kind = "module"
		}
		base.Fatalf("go: %v", err)
	}

	setDefaultBuildMod() // 可能启用自动售货
	rs = requirementsFromModFile()
	if cfg.BuildMod == "vendor" {
		readVendorList()
		checkVendorConsistency()
		rs.initVendor(vendorList)
	}
	if rs.hasRedundantRoot() {
		// 如果任何模块路径在根中出现多次，我们知道
		// go.mod文件需要更新，即使我们尚未加载任何
		// 可传递依赖项。
		rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
		if err != nil {
			base.Fatalf("go: %v", err)
		}
	}

	if index.goVersionV == "" {
		// TODO（#45551）：做一些更有原则的事情，而不是检查
		// 在此处直接输入cfg.CmdName。
		if cfg.BuildMod == "mod" && cfg.CmdName != "mod graph" && cfg.CmdName != "mod why" {
			addGoStmt(LatestGoVersion())
			if go117EnableLazyLoading {
				// 我们需要在go.mod文件中添加一个“go”版本，但我们必须假设
				// 其现有内容在Go 1.11和1.16之间匹配。
				// Go 1.11至1.16有迫切的要求，但最新的Go
				// 版本使用惰性需求，所以我们需要转换
				// 要求懒惰。
				rs, err = convertDepth(ctx, rs, lazy)
				if err != nil {
					base.Fatalf("go: %v", err)
				}
			}
		} else {
			rawGoVersion.Store(Target, modFileGoVersion())
		}
	}

	requirements = rs
	return requirements, true
}

// CreateModFile通过创建go.mod文件来初始化新模块。
// None
// 如果modPath为空，CreateModFile将尝试从
// GOPATH中的目录位置。
// None
// 如果存在供应商配置文件，CreateModFile将尝试
// 将其转换为go.mod指令。生成的生成列表可能不正确
// 与传统配置完全相同（例如，我们无法
// 来自同一模块的多个版本的软件包）。
func CreateModFile(ctx context.Context, modPath string) {
	modRoot = base.Cwd()
	Init()
	modFilePath := ModFilePath()
	if _, err := fsys.Stat(modFilePath); err == nil {
		base.Fatalf("go: %s already exists", modFilePath)
	}

	if modPath == "" {
		var err error
		modPath, err = findModulePath(modRoot)
		if err != nil {
			base.Fatalf("go: %v", err)
		}
	} else if err := module.CheckImportPath(modPath); err != nil {
		if pathErr, ok := err.(*module.InvalidPathError); ok {
			pathErr.Kind = "module"
			// 与build.IsLocalPath（）相同
			if pathErr.Path == "." || pathErr.Path == ".." ||
				strings.HasPrefix(pathErr.Path, "./") || strings.HasPrefix(pathErr.Path, "../") {
				pathErr.Err = errors.New("is a local import path")
			}
		}
		base.Fatalf("go: %v", err)
	}

	fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", modPath)
	modFile = new(modfile.File)
	modFile.AddModuleStmt(modPath)
	initTarget(modFile.Module.Mod)
	addGoStmt(LatestGoVersion()) // 在转换模块要求之前添加go指令。

	convertedFrom, err := convertLegacyConfig(modPath)
	if convertedFrom != "" {
		fmt.Fprintf(os.Stderr, "go: copying requirements from %s\n", base.ShortPath(convertedFrom))
	}
	if err != nil {
		base.Fatalf("go: %v", err)
	}

	rs := requirementsFromModFile()
	rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
	if err != nil {
		base.Fatalf("go: %v", err)
	}
	commitRequirements(ctx, modFileGoVersion(), rs)

	// 建议运行“go mod tidy”，除非项目为空。即使我们
	// 导入了上面所有正确的要求，我们可能缺少了
	// 一些总和，因此-mod=readonly中的下一个构建命令可能会失败。
	// None
	// 我们查找非隐藏的.go文件或子目录，以确定
	// 这是一个现有的项目。走在树上寻找包裹会更容易
	// 准确，但可能需要更长的时间。
	empty := true
	files, _ := os.ReadDir(modRoot)
	for _, f := range files {
		name := f.Name()
		if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_") {
			continue
		}
		if strings.HasSuffix(name, ".go") || f.IsDir() {
			empty = false
			break
		}
	}
	if !empty {
		fmt.Fprintf(os.Stderr, "go: to add module requirements and sums:\n\tgo mod tidy\n")
	}
}

// fixVersion返回使用查询函数实现的modfile.VersionFixer。
// None
// 它将提交哈希和分支名称解析为版本，
// 规范化早期vgo草案中出现的版本，
// 对于已经看起来是规范的版本，它什么也不做。
// None
// 如果VersionFixer返回非规范版本，则将其设置为“fixed”。
func fixVersion(ctx context.Context, fixed *bool) modfile.VersionFixer {
	return func(path, vers string) (resolved string, err error) {
		defer func() {
			if err == nil && resolved != vers {
				*fixed = true
			}
		}()

		// 特殊情况：删除旧的-gopkgin-hack。
		if strings.HasPrefix(path, "gopkg.in/") && strings.Contains(vers, "-gopkgin-") {
			vers = vers[strings.Index(vers, "-gopkgin-")+len("-gopkgin-"):]
		}

		// 在每种情况下都会推测性地调用fixVersion
		// 模块，每个go.mod文件的版本对。
		// 如果查询看起来正常，请避免查询。
		_, pathMajor, ok := module.SplitPathVersion(path)
		if !ok {
			return "", &module.ModuleError{
				Path: path,
				Err: &module.InvalidVersionError{
					Version: vers,
					Err:     fmt.Errorf("malformed module path %q", path),
				},
			}
		}
		if vers != "" && module.CanonicalVersion(vers) == vers {
			if err := module.CheckPathMajor(vers, pathMajor); err != nil {
				return "", module.VersionError(module.Version{Path: path, Version: vers}, err)
			}
			return vers, nil
		}

		info, err := Query(ctx, path, vers, "", nil)
		if err != nil {
			return "", err
		}
		return info.Version, nil
	}
}

// AllowMissingModuleImports允许将导入路径解析为模块
// 当没有模块根时。通常，这是禁止的，因为它很慢
// 没有办法让结果重现，但是有些命令
// 像“go-get”这样的人应该这样做。
// None
// 在模块外部时，此函数会影响默认的cfg.BuildMod，
// 因此只能在Init之前调用它。
func AllowMissingModuleImports() {
	if initialized {
		panic("AllowMissingModuleImports after Init")
	}
	allowMissingModuleImports = true
}

// initTarget根据modFile设置目标和相关变量，
func initTarget(m module.Version) {
	Target = m
	targetPrefix = m.Path

	if rel := search.InDir(base.Cwd(), cfg.GOROOTsrc); rel != "" {
		targetInGorootSrc = true
		if m.Path == "std" {
			// GOROOT/src中的“std”模块是Go标准库。不同于其他
			// “std”模块中的包没有导入路径前缀。
			// None
			// GOROOT/src之外名为“std”的模块不接收此特殊设置
			// 治疗，因此有可能在其他GOROOTs中运行“go测试”
			// 使用修改包的组合测试单个包
			// 和普通标准库。
			// （见https:
			targetPrefix = ""
		}
	}
}

// requirementsFromModFile从中返回一组未排除的需求
// 全局modFile。
func requirementsFromModFile() *Requirements {
	roots := make([]module.Version, 0, len(modFile.Require))
	direct := map[string]bool{}
	for _, r := range modFile.Require {
		if index != nil && index.exclude[r.Mod] {
			if cfg.BuildMod == "mod" {
				fmt.Fprintf(os.Stderr, "go: dropping requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
			} else {
				fmt.Fprintf(os.Stderr, "go: ignoring requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
			}
			continue
		}

		roots = append(roots, r.Mod)
		if !r.Indirect {
			direct[r.Mod.Path] = true
		}
	}
	module.Sort(roots)
	rs := newRequirements(modDepthFromGoVersion(modFileGoVersion()), roots, direct)
	return rs
}

// setDefaultBuildMod设置cfg.BuildMod的默认值，如果-mod标志
// 没有提供。setDefaultBuildMod可能被调用多次。
func setDefaultBuildMod() {
	if cfg.BuildModExplicit {
		// 不要重写显式“-mod=”参数。
		return
	}

	if cfg.CmdName == "get" || strings.HasPrefix(cfg.CmdName, "mod ") {
		// “get”和“go mod”命令可以自动更新go.mod。
		// 托多（杰康罗德）：这个应该窄一点吗？应该“下载”还是“下载”
		// “go mod graph”是否默认更新go.mod？
		cfg.BuildMod = "mod"
		return
	}
	if modRoot == "" {
		if allowMissingModuleImports {
			cfg.BuildMod = "mod"
		} else {
			cfg.BuildMod = "readonly"
		}
		return
	}

	if fi, err := fsys.Stat(filepath.Join(modRoot, "vendor")); err == nil && fi.IsDir() {
		modGo := "unspecified"
		if index != nil && index.goVersionV != "" {
			if semver.Compare(index.goVersionV, "v1.14") >= 0 {
				// Go版本至少为1.14，并且存在供应商目录。
				// 默认情况下，Set-mod=供应商。
				cfg.BuildMod = "vendor"
				cfg.BuildModReason = "Go version in go.mod is at least 1.14 and vendor directory exists."
				return
			} else {
				modGo = index.goVersionV[1:]
			}
		}

		// 由于存在供应商目录，我们应该记录为什么不使用它。
		// 此消息通常不会显示，但可能会出现导入错误。
		cfg.BuildModReason = fmt.Sprintf("Go version in go.mod is %s, so vendor directory was not used.", modGo)
	}

	cfg.BuildMod = "readonly"
}

// convertLegacyConfig从传统供应商导入模块需求
// 配置文件（如果存在）。
func convertLegacyConfig(modPath string) (from string, err error) {
	noneSelected := func(path string) (version string) { return "none" }
	queryPackage := func(path, rev string) (module.Version, error) {
		pkgMods, modOnly, err := QueryPattern(context.Background(), path, rev, noneSelected, nil)
		if err != nil {
			return module.Version{}, err
		}
		if len(pkgMods) > 0 {
			return pkgMods[0].Mod, nil
		}
		return modOnly.Mod, nil
	}
	for _, name := range altConfigs {
		cfg := filepath.Join(modRoot, name)
		data, err := os.ReadFile(cfg)
		if err == nil {
			convert := modconv.Converters[name]
			if convert == nil {
				return "", nil
			}
			cfg = filepath.ToSlash(cfg)
			err := modconv.ConvertLegacyConfig(modFile, cfg, data, queryPackage)
			return name, err
		}
	}
	return "", nil
}

// addGoStmt将go指令添加到go.mod文件（如果尚未添加）
// 包括一个。添加的“go”版本（如果有）是受支持的最新版本
// 通过这个工具链。
func addGoStmt(v string) {
	if modFile.Go != nil && modFile.Go.Version != "" {
		return
	}
	if err := modFile.AddGoStmt(v); err != nil {
		base.Fatalf("go: internal error: %v", err)
	}
	rawGoVersion.Store(Target, v)
}

// LatestGoVersion返回所支持的Go语言的最新版本
// 这个工具链，像“1.17”。
func LatestGoVersion() string {
	tags := build.Default.ReleaseTags
	version := tags[len(tags)-1]
	if !strings.HasPrefix(version, "go") || !modfile.GoVersionRE.MatchString(version[2:]) {
		base.Fatalf("go: internal error: unrecognized default version %q", version)
	}
	return version[2:]
}

// Priorgoverion返回紧跟在v之前的Go major版本，
// 或者v本身，如果v是第一个主要发行版（1.0）或不受支持
// Go版本。
func priorGoVersion(v string) string {
	vTag := "go" + v
	tags := build.Default.ReleaseTags
	for i, tag := range tags {
		if tag == vTag {
			if i == 0 {
				return v
			}

			version := tags[i-1]
			if !strings.HasPrefix(version, "go") || !modfile.GoVersionRE.MatchString(version[2:]) {
				base.Fatalf("go: internal error: unrecognized version %q", version)
			}
			return version[2:]
		}
	}
	return v
}

var altConfigs = []string{
	"Gopkg.lock",

	"GLOCKFILE",
	"Godeps/Godeps.json",
	"dependencies.tsv",
	"glide.lock",
	"vendor.conf",
	"vendor.yml",
	"vendor/manifest",
	"vendor/vendor.json",

	".git/config",
}

func findModuleRoot(dir string) (root string) {
	if dir == "" {
		panic("dir not set")
	}
	dir = filepath.Clean(dir)

	// 查找封闭的go.mod。
	for {
		if fi, err := fsys.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
			return dir
		}
		d := filepath.Dir(dir)
		if d == dir {
			break
		}
		dir = d
	}
	return ""
}

func findAltConfig(dir string) (root, name string) {
	if dir == "" {
		panic("dir not set")
	}
	dir = filepath.Clean(dir)
	if rel := search.InDir(dir, cfg.BuildContext.GOROOT); rel != "" {
		// 不要建议从$GOROOT/.git/config创建模块
		// 或者$GOROOT的任何父级中的配置文件（请参见#34191）。
		return "", ""
	}
	for {
		for _, name := range altConfigs {
			if fi, err := fsys.Stat(filepath.Join(dir, name)); err == nil && !fi.IsDir() {
				return dir, name
			}
		}
		d := filepath.Dir(dir)
		if d == dir {
			break
		}
		dir = d
	}
	return "", ""
}

func findModulePath(dir string) (string, error) {
	// TODO（bcmills）：一旦我们找到了一个合理的模块路径，我们应该
	// 查询版本控制（如果可用）以验证它是否与主版本匹配
	// 最新标记的版本。
	// 请参阅https:
	// https:

	// 四处寻找进口评论，
	// 首先在顶级目录中，然后在子目录中。
	list, _ := os.ReadDir(dir)
	for _, info := range list {
		if info.Type().IsRegular() && strings.HasSuffix(info.Name(), ".go") {
			if com := findImportComment(filepath.Join(dir, info.Name())); com != "" {
				return com, nil
			}
		}
	}
	for _, info1 := range list {
		if info1.IsDir() {
			files, _ := os.ReadDir(filepath.Join(dir, info1.Name()))
			for _, info2 := range files {
				if info2.Type().IsRegular() && strings.HasSuffix(info2.Name(), ".go") {
					if com := findImportComment(filepath.Join(dir, info1.Name(), info2.Name())); com != "" {
						return path.Dir(com), nil
					}
				}
			}
		}
	}

	// 查找声明导入路径的Godeps.json。
	data, _ := os.ReadFile(filepath.Join(dir, "Godeps/Godeps.json"))
	var cfg1 struct{ ImportPath string }
	json.Unmarshal(data, &cfg1)
	if cfg1.ImportPath != "" {
		return cfg1.ImportPath, nil
	}

	// 查找声明导入路径的vendor.json。
	data, _ = os.ReadFile(filepath.Join(dir, "vendor/vendor.json"))
	var cfg2 struct{ RootPath string }
	json.Unmarshal(data, &cfg2)
	if cfg2.RootPath != "" {
		return cfg2.RootPath, nil
	}

	// 在GOPATH中查找路径。
	var badPathErr error
	for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
		if gpdir == "" {
			continue
		}
		if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." {
			path := filepath.ToSlash(rel)
			// gorelease将提醒发布模块的用户修复路径。
			if err := module.CheckImportPath(path); err != nil {
				badPathErr = err
				break
			}
			return path, nil
		}
	}

	reason := "outside GOPATH, module path must be specified"
	if badPathErr != nil {
		// 如果模块位于GOPATH中，则返回不同的错误消息，但
		// 上面确定的模块路径将是无效路径。
		reason = fmt.Sprintf("bad module path inferred from directory in GOPATH: %v", badPathErr)
	}
	msg := `cannot determine module path for source directory %s (%s)

Example usage:
	'go mod init example.com/m' to initialize a v0 or v1 module
	'go mod init example.com/m/v2' to initialize a v2 module

Run 'go help mod init' for more information.
`
	return "", fmt.Errorf(msg, dir, reason)
}

var (
	importCommentRE = lazyregexp.New(`(?m)^package[ \t]+[^ \t\r\n/]+[ \t]+// [\t]+导入[\t]+（\“[^”]+\”[\t]*\r？\n`）
)

func findImportComment(file string) string {
	data, err := os.ReadFile(file)
	if err != nil {
		return ""
	}
	m := importCommentRE.FindSubmatch(data)
	if m == nil {
		return ""
	}
	path, err := strconv.Unquote(string(m[1]))
	if err != nil {
		return ""
	}
	return path
}

var allowWriteGoMod = true

// 不允许WriteGoMod会导致将来对WriteGoMod的调用完全不执行任何操作。
func DisallowWriteGoMod() {
	allowWriteGoMod = false
}

// AllowWriteGoMod撤消DisallowWriteGoMod的效果：
// 如果需要，未来对WriteGoMod的调用将更新go.mod。
// 请注意，任何过去的调用都已被丢弃，因此通常
// 对AlowWriteGoMod的调用之后应该是对WriteGoMod的调用。
func AllowWriteGoMod() {
	allowWriteGoMod = true
}

// WriteGoMod将当前生成列表写回go.mod。
func WriteGoMod(ctx context.Context) {
	if !allowWriteGoMod {
		panic("WriteGoMod called while disallowed")
	}
	commitRequirements(ctx, modFileGoVersion(), LoadModFile(ctx))
}

// CommitterRequests写入将全局需求变量设置为rs和
// 将其内容写回磁盘上的go.mod文件。
func commitRequirements(ctx context.Context, goVersion string, rs *Requirements) {
	requirements = rs

	if !allowWriteGoMod {
		// modload之外的一些包承诺稍后更新go.mod文件。
		return
	}

	if modRoot == "" {
		// 我们不在一个模块中，所以我们没有地方写go.mod文件。
		return
	}

	var list []*modfile.Require
	for _, m := range rs.rootModules {
		list = append(list, &modfile.Require{
			Mod:      m,
			Indirect: !rs.direct[m.Path],
		})
	}
	if goVersion != "" {
		modFile.AddGoStmt(goVersion)
	}
	if semver.Compare("v"+modFileGoVersion(), separateIndirectVersionV) < 0 {
		modFile.SetRequire(list)
	} else {
		modFile.SetRequireSeparateIndirect(list)
	}
	modFile.Cleanup()

	dirty := index.modFileIsDirty(modFile)
	if dirty && cfg.BuildMod != "mod" {
		// 如果由于-mod=readonly，我们即将失败，
		// 宁愿报告脏go.mod而不是脏go.sum
		base.Fatalf("go: %v", errGoModDirty)
	}

	if !dirty && cfg.CmdName != "mod tidy" {
		// go.mod文件具有与以前相同的语义内容
		// （但不一定是完全相同的字节）。
		// 不要写go.mod，但要写go.sum，以防我们添加或删减总和。
		// “go mod init”不应写入go.sum，因为它将不完整。
		if cfg.CmdName != "mod init" {
			modfetch.WriteGoSum(keepSums(ctx, loaded, rs, addBuildListZipSums))
		}
		return
	}
	gomod := ModFilePath()
	if _, ok := fsys.OverlayPath(gomod); ok {
		if dirty {
			base.Fatalf("go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay")
		}
		return
	}

	new, err := modFile.Format()
	if err != nil {
		base.Fatalf("go: %v", err)
	}
	defer func() {
		// 此时，我们决定将磁盘上的go.mod文件设置为new。
		index = indexModFile(new, modFile, false)

		// 释放侧锁并刷新索引后更新go.sum。
		// “go mod init”不应写入go.sum，因为它将不完整。
		if cfg.CmdName != "mod init" {
			modfetch.WriteGoSum(keepSums(ctx, loaded, rs, addBuildListZipSums))
		}
	}()

	// 尽最大努力获取侧锁，仅排除
	// “go”命令的早期版本无法同时进行编辑。
	if unlock, err := modfetch.SideLock(); err == nil {
		defer unlock()
	}

	errNoChange := errors.New("no update needed")

	err = lockedfile.Transform(ModFilePath(), func(old []byte) ([]byte, error) {
		if bytes.Equal(old, new) {
			// go.mod文件已经等于new，可能是由于某些原因
			// 其他过程。
			return nil, errNoChange
		}

		if index != nil && !bytes.Equal(old, index.data) {
			// go.mod文件的内容已更改。理论上我们可以加上
			// 将新模块添加到生成列表中，重新计算，并检查是否有
			// *我们的*构建列表中的模块被升级到另一个版本，但这是错误的
			// 为了边际利益做了大量的工作。相反，请使命令失败：if users
			// 要运行并发命令，它们需要从一个完整的，
			// 一致的模块定义。
			return nil, fmt.Errorf("existing contents have changed since last read")
		}

		return new, nil
	})

	if err != nil && err != errNoChange {
		base.Fatalf("go: updating go.mod: %v", err)
	}
}

// keepSums返回一组模块（和go.mod文件项），这些模块
// 需要校验和才能重新加载同一组包
// 由最近对LoadPackages或ImportFromFiles的调用加载，
// 包括重建MVS结果所需的任何go.mod文件，
// 除了keepMods中每个模块的校验和之外。
func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums) map[module.Version]bool {
	// 完整模块图中的每个模块都提供其需求，
	// 因此，为了确保构建列表本身是可复制的，
	// 我们需要图中每个go.mod的总和（无论
	// 已选择该版本）。
	keep := make(map[module.Version]bool)

	// 为生成列表中的模块添加带有前缀路径的条目
	// 加载包的路径。我们需要保留所有这些模块的总和-
	// 不只是包含实际包的模块-为了排除
	// 下次加载包时出现不明确的导入错误。
	if ld != nil {
		for _, pkg := range ld.pkgs {
			// 我们在这里检查pkg.mod.Path而不是pkg.inStd，因为
			// 伪包“C”不在std中，但不由任何模块提供（和
			// 不应强制加载整个模块图）。
			if pkg.testOf != nil || (pkg.mod.Path == "" && pkg.err == nil) || module.CheckImportPath(pkg.path) != nil {
				continue
			}

			if rs.depth == lazy && pkg.mod.Path != "" {
				if v, ok := rs.rootSelected(pkg.mod.Path); ok && v == pkg.mod.Version {
					// pkg是从根模块加载的，因为主模块是
					// 我们不检查包的非根模块冲突
					// 这可以在根中找到。所以我们只需要
					// 可能包含pkg的根模块，而不是所有可能的模块。
					for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
						if v, ok := rs.rootSelected(prefix); ok && v != "none" {
							m := module.Version{Path: prefix, Version: v}
							keep[resolveReplacement(m)] = true
						}
					}
					continue
				}
			}

			mg, _ := rs.Graph(ctx)
			for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
				if v := mg.Selected(prefix); v != "none" {
					m := module.Version{Path: prefix, Version: v}
					keep[resolveReplacement(m)] = true
				}
			}
		}
	}

	if rs.graph.Load() == nil {
		// 未加载模块图，可能是因为主模块是惰性的
		// 或者可能是因为我们还不需要加载图表。
		// 保存根模块（或其替换项）的总和，但不要
		// 仅为查找和保留总和而加载图形的成本。
		for _, m := range rs.rootModules {
			r := resolveReplacement(m)
			keep[modkey(r)] = true
			if which == addBuildListZipSums {
				keep[r] = true
			}
		}
	} else {
		mg, _ := rs.Graph(ctx)
		mg.WalkBreadthFirst(func(m module.Version) {
			if _, ok := mg.RequiredBy(m); ok {
				// m的go.mod文件中的需求显示在模块图中，
				// 因此，它们与MVS结果相关，无论m是否为
				// 实际选中的。
				keep[modkey(resolveReplacement(m))] = true
			}
		})

		if which == addBuildListZipSums {
			for _, m := range mg.BuildList() {
				keep[resolveReplacement(m)] = true
			}
		}
	}

	return keep
}

type whichSums int8

const (
	loadedZipSumsOnly = whichSums(iota)
	addBuildListZipSums
)

// modKey返回module.Version，在该版本下m的go.mod的校验和
// 文件存储在go.sum文件中。
func modkey(m module.Version) module.Version {
	return module.Version{Path: m.Path, Version: m.Version + "/go.mod"}
}
